package tech.horgee.schedule;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import tech.horgee.utils.RedisUtil;

import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 并发定时任务配置
 */
@Slf4j
@Configuration
@EnableScheduling
@RequiredArgsConstructor
@EnableConfigurationProperties({TaskProperties.class})
public class ScheduleConfig implements SchedulingConfigurer {
    private final ApplicationContext applicationContext;
    private final TaskProperties taskProperties;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskScheduler());
        List<TaskItem> taskList = taskProperties.getList();
        // 没有任务、开关没开 直接返回
        if (taskList.isEmpty() || !taskProperties.isEnabled()) return;
        taskList.forEach(task -> {
            String taskName = task.getName();
            if (!task.isEnabled()) return;
            // 注册定时任务
            taskRegistrar.addCronTask(() -> {
                long tid = Thread.currentThread().getId();
                log.info("[TID:{}] 执行定时任务「{}」{}", tid, taskName, task.getRemark());
                RLock lock = RedisUtil.getLock(taskName);
                try {
                    // 尝试获取定义了 leaseTime 的锁。如有必要，等待定义的 waitTime ，直到锁可用。
                    if (lock.tryLock(task.getWaitTime().getSeconds(), task.getLeaseTime().getSeconds(), TimeUnit.SECONDS)) {
                        ICronTask taskItem = applicationContext.getBean(taskName, ICronTask.class);
                        taskItem.run();
                    } else {
                        log.info("[TID:{}] 未获取到锁，定时任务不执行「{}」{}", tid, taskName, task.getRemark());
                    }
                } catch (InterruptedException e) {
                    log.error("任务执行失败", e);
                }
            }, task.getCron());
            log.info("注册定时任务「{}」{}", taskName, task);
        });
    }

    /**
     * &#064;EnableScheduling 加载顺序: SchedulingConfigurer -> TaskScheduler -> ScheduledExecutorService，
     * TaskScheduler 和 ScheduledExecutorService 会优先根据类型找，如果存在多个同类型的，则根据默认的名字 taskScheduler 来找，
     * 如果 taskScheduler 始终未被初始化，则会使用默认的单线程执行程序 newSingleThreadScheduledExecutor()
     * <a href="https://blog.csdn.net/a718515028/article/details/80396102">原文链接</a>
     */
    @Bean(destroyMethod = "shutdown")
    public ScheduledExecutorService taskScheduler() {
        return Executors.newScheduledThreadPool(taskProperties.getPoolSize());
    }
}
